package redora.client.mvp;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.place.shared.*;
import com.google.gwt.place.shared.Place;
import com.google.gwt.user.client.Window;

import java.util.logging.Logger;

/**
 * @author Nanjing RedOrange (www.red-orange.cn)
 */
public class PlaceController extends com.google.gwt.place.shared.PlaceController {

    private static final Logger log = Logger.getLogger(PlaceController.class.getName());

    private final EventBus eventBus;

    private final Delegate delegate;
    private Place where = Place.NOWHERE;

    /**
     * Create a new PlaceController with a {@link DefaultDelegate}. The
     * DefaultDelegate is created via a call to GWT.create(), so an alternative
     * default implementation can be provided through &lt;replace-with&gt; rules
     * in a {@code .gwt.xml} file.
     *
     * @param eventBus the {@link EventBus}
     */
    public PlaceController(EventBus eventBus) {
        super(eventBus);
        this.eventBus = eventBus;
        this.delegate = GWT.create(DefaultDelegate.class);
        delegate.addWindowClosingHandler(new Window.ClosingHandler() {
            public void onWindowClosing(Window.ClosingEvent event) {
                String warning = maybeGoTo(com.google.gwt.place.shared.Place.NOWHERE);
                if (warning != null) {
                    event.setMessage(warning);
                }
            }
        });
    }

    /**
     * Create a new PlaceController.
     *
     * @param eventBus the {@link EventBus}
     * @param delegate the {@link Delegate} in charge of Window-related events
     */
    public PlaceController(EventBus eventBus, Delegate delegate) {
        super(eventBus, delegate);
        this.eventBus = eventBus;
        this.delegate = delegate;
        delegate.addWindowClosingHandler(new Window.ClosingHandler() {
            public void onWindowClosing(Window.ClosingEvent event) {
                String warning = maybeGoTo(com.google.gwt.place.shared.Place.NOWHERE);
                if (warning != null) {
                    event.setMessage(warning);
                }
            }
        });
    }

    /**
     * Returns the current place.
     *
     * @return a {@link com.google.gwt.place.shared.Place} instance
     */
    public com.google.gwt.place.shared.Place getWhere() {
        return where;
    }

    /**
     * Request a change to a new place. It is not a given that we'll actually get
     * there. First a {@link PlaceChangeRequestEvent} will be posted to the
     * event bus. If any receivers post a warning message to that event, it will
     * be presented to the user via {@link Delegate#confirm(String)} (which is
     * typically a call to {@link Window#confirm(String)}). If she cancels, the
     * current location will not change. Otherwise, the location changes and a
     * {@link PlaceChangeEvent} is posted announcing the new place.
     *
     * @param newPlace a {@link com.google.gwt.place.shared.Place} instance
     */
    public void goTo(com.google.gwt.place.shared.Place newPlace) {
        log().fine("goTo: " + newPlace);

        if (getWhere().equals(newPlace)) {
            log().fine("Asked to return to the same place: " + newPlace);
            return;
        }

        String warning = maybeGoTo(newPlace);
        if (warning == null || delegate.confirm(warning)) {
            where = newPlace;
            eventBus.fireEvent(new PlaceChangeEvent(newPlace));
        }
    }

    public void goTo(com.google.gwt.place.shared.Place newPlace, boolean checkIsNew) {
        log().fine("goTo: " + newPlace);

        if (getWhere().equals(newPlace) && checkIsNew) {
            log().fine("Asked to return to the same place: " + newPlace);
            return;
        }

        String warning = maybeGoTo(newPlace);
        if (warning == null || delegate.confirm(warning)) {
            where = newPlace;
            eventBus.fireEvent(new PlaceChangeEvent(newPlace));
        }
    }

    /**
     * Visible for testing.
     */
    Logger log() {
        return log;
    }

    private String maybeGoTo(Place newPlace) {
        PlaceChangeRequestEvent willChange = new PlaceChangeRequestEvent(
                newPlace);
        eventBus.fireEvent(willChange);
        String warning = willChange.getWarning();
        return warning;
    }
}
